package com.ithailong.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

/**
 * author:hlc
 * date:2023/12/25
 */
public class IthailongApplication {
   private Class configClass;
   private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
   private ConcurrentHashMap<String, Object> singletonObject = new ConcurrentHashMap<>();

   //   创建IthailongApplication的有参构造（参数为传入的一个Class类）该传入的类是一个扫描类，扫描所配置的包此处扫描到的是（com.ithailong.service）扫描这下面的所有类
   public IthailongApplication(Class configClass) {
      this.configClass = configClass;
//      扫描==》BeanDefiniton--->存储到beanDefinitionMap中
//      通过isAnnotationPresent检测该类上是否存在ComponentScan注解
      if (configClass.isAnnotationPresent(ComponentScan.class)) {
//         接受到一个声明式注解
//         使用java的反射，获取configClass类上的ComponentScan注解
         ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
//         拿到注解中的value，也就是要扫描的包（com.ithialong.service）
         String path = componentScanAnnotation.value();//扫描路径，ocm.ithailong.service
         path = path.replace(".", "/");//修改路径中的点为/(相对路径)
//         .getClassLoader()是调用Class对象的方法，返回该类的类加载器对象
         ClassLoader classLoader = IthailongApplication.class.getClassLoader();//拿到类加载器
//         通过类加载器拿到类的路径
         URL resource = classLoader.getResource(path);//传给我一个路径
         File file = new File(resource.getFile());
         System.out.println(file);
         if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File file1 : files) {
//               过滤将.class文件筛选出来
               String fileName = file1.getAbsolutePath();//拿到绝对路径
               System.out.println(fileName);
//               判断当前的文件是否已.class结尾的(判断是否是一个类)
               if (fileName.endsWith(".class")) {
//                  截取类的全限定名(com/ithailong/service/UserService)
                  String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                  className = className.replace("\\", ".");
                  System.out.println(className);
//                   通过反射判断类中是否有注解
                  try {
                     Class<?> clazz = classLoader.loadClass(className);
                     if (clazz.isAnnotationPresent(Component.class)) {
                        Component component = clazz.getAnnotation(Component.class);
                        String beanName = component.value();
//                        如果beanName为空则是因为使用了注解中的默认值“”
                        if (beanName.equals("")) {
//                           使用
                           beanName = Introspector.decapitalize(clazz.getSimpleName());
                        }
//Bean
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setType(clazz);
                        if (clazz.isAnnotationPresent(Scope.class)) {
                           Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                           beanDefinition.setScope(scopeAnnotation.value());
                        } else {
                           beanDefinition.setScope("singleton");
                        }
                        beanDefinitionMap.put(beanName, beanDefinition);
                     }
                  } catch (ClassNotFoundException e) {
                     throw new RuntimeException(e);
                  }
               }
            }
         }
      }
//      实例化单例bean
      for (String beanName : beanDefinitionMap.keySet()) {
         BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
         if (beanDefinition.getScope().equals("singleton")) {
            Object bean = creatBean(beanName, beanDefinition);
            singletonObject.put(beanName, bean);
         }
      }
   }

   private Object creatBean(String beanName, BeanDefinition beanDefinition) {
      Class clazz = beanDefinition.getType();
      try {
         Object instance = clazz.getConstructor().newInstance();
//         对一个bean来说，先对其进行实例化，然后在实现依赖注入
         for (Field field : clazz.getDeclaredFields()) {
//            判断当前的属性中是否存在AutoWired注解
            if (field.isAnnotationPresent(Autowired.class)) {
               field.setAccessible(true);
               field.set(instance, getBean(field.getName()));
            }
         }
//         Aware回调
         if (instance instanceof BeanNameAware) {
            ((BeanNameAware) instance).setBeanName(beanName);
         }
//         初始化
         if (instance instanceof InitializingBean){
            ((InitializingBean)instance).afterPropertiesSet();
         }
//         初始化后AOP

         return instance;
      } catch (InstantiationException e) {
         throw new RuntimeException(e);
      } catch (IllegalAccessException e) {
         throw new RuntimeException(e);
      } catch (InvocationTargetException e) {
         throw new RuntimeException(e);
      } catch (NoSuchMethodException e) {
         throw new RuntimeException(e);
      }
   }

   public Object getBean(String beanName) {
      BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
      if (beanDefinition == null) {
         throw new NullPointerException();
      } else {
         String scope = beanDefinition.getScope();
         if (scope.equals("singleton")) {
            Object bean = singletonObject.get(beanName);
            if (bean == null) {
               Object bean1 = creatBean(beanName, beanDefinition);
               singletonObject.put(beanName, bean1);
            }
            return bean;
         } else {
//            多例bean
            return creatBean(beanName, beanDefinition);
         }
      }
   }
}
